This notebook defines the most focal recurrent copy number units by removing focal changes that are within entire chromosome arm losses and gains. Most focal here meaning:

  • If a chromosome arm is not clearly defined as a gain or loss (and is callable) we look to define the cytoband level status
  • If a cytoband is not clearly defined as a gain or loss (and is callable) we then look to define the gene-level status

Usage

This notebook is intended to be run from the command line with the following (assumes you are in the root directory of the repository):

Rscript -e "rmarkdown::render('analyses/focal-cn-file-preparation/05-define-most-focal-cn-units.Rmd', clean = TRUE)"

Cutoffs:

# The percentage of calls a particular status needs to be 
# above to be called the majority status -- the decision
# for a cutoff of 90% here was made to ensure that the status
# is not only the majority status but it is also significantly
# called more than the other status values in the region
percent_threshold <- 0.9

# The percentage threshold for determining if enough of a region
# (arm, cytoband, or gene) is callable to determine its status --
# the decision for a cutoff of 50% here was made as it seems reasonable
# to expect a region to be more than 50% callable for a dominant status
# call to be made
uncallable_threshold <- 0.5

Set up

Libraries and functions

library(tidyverse)

Custom Function

status_majority_caller <- function(status_df,
                                   region_variable,
                                   status_column_name,
                                   percent_threshold_value, 
                                   uncallable_threshold_value = uncallable_threshold) {
  # Given a data.frame with cytoband/gene-level copy number status data,
  # find the dominant status of the cytoband/gene region by calculating
  # the percentages of the region that each call represents.
  #
  # Args:
  #   status_df: data.frame with cytoband/gene-level copy number status data
  #   region_variable: string of the column name/region to calculate copy number
  #                    status percentages for
  #   status_column_name: string of the column name that holds the relevant copy
  #                       number status data
  #   percent_threshold_value: What percent of calls a particular status needs to be 
  #                      above to be called the majority. 
  #   uncallable_threshold_value: a threshold for determining if enough of a region is 
  #                         callable to determine its status. 
  #   
  # Return:
  #   status_count: data.frame with percentage values for each unique status in
  #                 each unique region (arm/cytoband/gene) and the dominant
  #                 status for that region
  
  # Tidyeval for these columns 
  region_sym <- rlang::sym(region_variable)
  status_sym <- rlang::sym(status_column_name)
  
  # Format the data and group it
  status_count <- status_df %>%
    count(!!region_sym, !!status_sym) %>%
    # Spread the data -- each row represents a unique chromosome arm
    spread(!!status_sym, n) %>%
    # Turn NAs into 0s
    replace_na(list(
      gain = 0,
      loss = 0,
      neutral = 0,
      amplification = 0,
      uncallable = 0,
      unstable = 0
    )) %>%
    # Getting counts by region 
    group_by(!!region_sym) 
  
  # Let's store the regions separately so as to avoid weird coercions
  region_vector <- 
    status_count %>% 
    dplyr::pull(!!region_sym)
  
  # Calculate percent 
  status_count <- status_count %>%
    # Store region variable column out of the way as rownames
    tibble::column_to_rownames(region_variable) %>% 
    # Obtain a total counts variable column
    dplyr::mutate(total = apply(., 1, sum)) %>% 
    # Get the ratio of each status count to the total
    dplyr::mutate_at(dplyr::vars(-total), dplyr::funs(. / total)) %>% 
    # Bring back our region variable as its own column
    dplyr::mutate(!!region_variable := region_vector)

  # The logic here is to define the region status based on the majority of calls
  # in the region -- if the number of calls for a specific status is 
  # responsible for more than `percent_threshold` value of the total calls in that
  # region, then it is used to define the region's status (exception is for the 
  # uncallable status where we define the region as uncallable when more than the
  # `uncallable_threshold` of the calls in that region are uncallable)
  if ((region_variable == "chromosome_arm") | (region_variable == "gene_symbol")) {
    status_count <- status_count %>%
      mutate(
        dominant_status = case_when(
          gain > percent_threshold ~ "gain",
          loss > percent_threshold ~ "loss",
          amplification > percent_threshold ~ "amplification",
          TRUE ~ "neutral"
        )
      )
  } else if (region_variable == "cytoband") {
    status_count <- status_count %>%
      mutate(
        dominant_status = case_when(
          uncallable > uncallable_threshold ~ "uncallable",
          gain > percent_threshold~ "gain",
          loss > percent_threshold ~ "loss",
          neutral > percent_threshold ~ "neutral",
          TRUE ~ "unstable"
        )
      )
  }

  return(status_count)
}
plot_dominant_status_calls <- function(status_count_df,
                                       region_variable) {
  # Given a data.frame with the percentage values for each region and the
  # dominant status for that region, plot the dominant status call on the
  # x-axis with the percentage values on the y-axis.
  #
  # Args:
  #   status_count_df: data.frame with percentage values for each unique status
  #                    in each unique region (arm/cytoband/gene) and the
  #                    dominant status for that region
  #   region_variable: string of the region (which will also be a column name)
  #                    that the data.frame holds percentage values for
  # Return:
  #   status_plot: plot representing the dominant status call on the x-axis and
  #                the percentage values on the y-axis
  
  status_count_df %>%
    # Remove the total column -- we don't want to plot this
    select(-total) %>%
    dplyr::ungroup() %>%
    # Store the non-percentage value column variable as rownames
    tibble::column_to_rownames(region_variable) %>%
    # Gather the data.frame to have columns and values in the format of
    # our status call, the percentage of total calls that status call is
    # responisble for, and the dominant status call made based on the
    # percentage value
    tidyr::gather(status, percent,-dominant_status) %>%
    # Plot our focal status values on the x-axis and the percentage values
    # on the y-axis
    ggplot2::ggplot(ggplot2::aes(x = status,
                                 y = percent)) +
    ggplot2::geom_jitter() +
    # Facet wrap around each dominant status value
    ggplot2::facet_wrap( ~ dominant_status)
}

Files and directories

results_dir <- "results"

# Define a logical object for running in CI
running_in_ci <- params$is_ci

Read in files

Read in cytoband status file and format it for what we will need in this notebook.

# Read in the file with consensus CN status data and the UCSC cytoband data --
# generated in `03-add-cytoband-status-consensus.Rmd`
consensus_seg_cytoband_status_df <-
  read_tsv(file.path("results", "consensus_seg_with_ucsc_cytoband_status.tsv.gz")) %>%
  # Need this to not have `chr`
  mutate(chr = gsub("chr", "", chr),
         cytoband = paste0(chr, cytoband)) %>%
  select(
    chromosome_arm,
    # Distinguish this dominant status that is based on cytobands, from the status 
    dominant_cytoband_status = dominant_status,
    cytoband,
    Kids_First_Biospecimen_ID
  )
Parsed with column specification:
cols(
  Kids_First_Biospecimen_ID = col_character(),
  chr = col_character(),
  cytoband = col_character(),
  dominant_status = col_character(),
  band_length = col_double(),
  callable_fraction = col_double(),
  gain_fraction = col_double(),
  loss_fraction = col_double(),
  chromosome_arm = col_character()
)

Read in the chromosome-level and gene-level data.

# Read in the annotated CN file (without the UCSC data)
consensus_seg_autosomes_df <-
  read_tsv(file.path(results_dir, "consensus_seg_annotated_cn_autosomes.tsv.gz"))
Parsed with column specification:
cols(
  biospecimen_id = col_character(),
  status = col_character(),
  copy_number = col_double(),
  ploidy = col_double(),
  ensembl = col_character(),
  gene_symbol = col_character(),
  cytoband = col_character()
)

Joining the gene-level, cytoband-level, and arm-level data into one data frame.

combined_status_df <- consensus_seg_autosomes_df %>%
  inner_join(
    consensus_seg_cytoband_status_df,
    by = c("biospecimen_id" = "Kids_First_Biospecimen_ID", "cytoband")
  )

Define most focal units

Determine chromosome arm status

# Use our custom function to make the status calls
arm_status_count <- status_majority_caller(
  combined_status_df,
  "chromosome_arm",
  "status", 
  percent_threshold = percent_threshold
)

# Display table
arm_status_count %>%
  group_by(dominant_status)

These are the chromosome arms that have not been defined as gain or loss – we want to define their cytoband/gene-level status

# Let's get a vector of the neutral arms
neutral_arms <- arm_status_count %>%
  filter(dominant_status == "neutral") %>% 
  dplyr::pull("chromosome_arm")

Determine cytoband status

We want to include cytoband and gene-level calls for chromosome arms that have not been defined as a gain or loss.

# Filter the annotated CN data to include only neutral chromosome arms
neutral_status_arm_df <- combined_status_df %>%
  filter(chromosome_arm %in% neutral_arms)

Making cytoband-level majority calls.

if (!(running_in_ci)) {
# Now count the cytoband level calls (for each status call) and define
# the cytoband as that status if more than 50% of the total counts are
# for that particular status
cytoband_status_count <- status_majority_caller(
  neutral_status_arm_df,
  "cytoband",
  "dominant_cytoband_status",
  percent_threshold = percent_threshold
)

# Display table
cytoband_status_count
}

Determine gene-level status

if (!(running_in_ci)) {
  # These are the cytobands that have not been defined as gain or loss --
  # we want to define their gene-level status
  neutral_cytobands <- cytoband_status_count %>%
    filter(dominant_status %in% c("unstable", "neutral")) %>%
    dplyr::pull("cytoband")

  # Filter the annotated CN data to include only these cytobands
  neutral_status_cytoband_df <- combined_status_df %>%
    filter(cytoband %in% neutral_cytobands)
  
  # Now count the gene-level calls (for each status call) and define
  # the gene as that status if more than 50% of the total counts are
  # for that particular status
  gene_status_count <- status_majority_caller(neutral_status_cytoband_df,
                                              "gene_symbol",
                                              "status", 
                                              percent_threshold = percent_threshold)
  
  # Display table
  gene_status_count
}

Plot calls

Plot the final dominant status call on the x-axis and the percent of each status on the y-axis.

Plot chromosome arm status calls

# Run `plot_dominant_status_calls` function for the chromosome arm calls
plot_dominant_status_calls(
  arm_status_count,
  "chromosome_arm"
)

Plot cytoband status calls

# Run `plot_dominant_status_calls` function for the cytoband calls if not
# running in CI
if (!(running_in_ci)) {
  plot_dominant_status_calls(cytoband_status_count,
                             "cytoband")
}

Plot gene status calls

# Plot if not running in circleCI
if (!(running_in_ci)) {
  plot_dominant_status_calls(gene_status_count,
                             "gene_symbol")
}

Combine arm, cytoband, and gene-level status data

# The logic variable `running_in_ci` is needed here because the CI testing
# files do not contain any of the genes in the `gene_status_count` data.frame
# generated above (when `running_in_ci` == FALSE)
if (!(running_in_ci)) {
  combined_status_count_df <- consensus_seg_autosomes_df %>%
    mutate(chromosome_arm = gsub("(p|q).*", "\\1", cytoband)) %>%
    inner_join(arm_status_count,
               by = "chromosome_arm") %>%
    left_join(cytoband_status_count,
              by = "cytoband",
              suffix = c(".arm", ".cytoband")) %>%
    left_join(gene_status_count,
              by = "gene_symbol",
              suffix = c(".arm", ".gene")) %>%
    mutate(
      focal_call = paste0(
        dominant_status.arm,
        ", ",
        dominant_status.cytoband,
        ", ",
        dominant_status
      ),
      # Here we want to define the most focal call based on the arm, cytoband,
      # and gene status information -- if a loss/gain is defined at the arm
      # level then the focal call will be "arm_loss" or "arm_gain" respectively,
      # and so on.
      focal_call = case_when(focal_call == "loss, NA, NA" ~ "arm_loss",
                             focal_call == "neutral, uncallable, NA" ~ "uncallable",
                             focal_call == "neutral, loss, NA" ~ "cytoband_loss",
                             focal_call == "neutral, NA, NA" ~ "arm_neutral",
                             focal_call == "neutral, unstable, neutral" ~ "gene_neutral",
                             focal_call == "neutral, unstable, loss" ~ "gene_loss",
                             TRUE ~ "Other")
    )
} else {
  combined_status_count_df <- consensus_seg_autosomes_df %>%
    mutate(chromosome_arm = gsub("(p|q).*", "\\1", cytoband)) %>%
    inner_join(arm_status_count, by = "chromosome_arm") %>%
    mutate(focal_call = dominant_status)
}

# Display combined status counts table
combined_status_count_df %>%
  arrange(focal_call) %>%
  group_by(biospecimen_id, focal_call)

Transform data into long format

Tranform the chromosome arm status data

if (!(running_in_ci)) {
  final_arm_status_df <- combined_status_count_df %>%
    # Filter to only non-neutral chromosome arms
    filter(dominant_status.arm != "neutral") %>%
    select(
      Kids_First_Biospecimen_ID = biospecimen_id,
      region = chromosome_arm,
      status = dominant_status.arm
    ) %>%
    distinct() %>%
    mutate(region_type = "chromosome_arm")
} else {
  final_arm_status_df <- combined_status_count_df %>%
    # Filter to only non-neutral chromosome arms
    filter(dominant_status != "neutral") %>%
    select(
      Kids_First_Biospecimen_ID = biospecimen_id,
      region = chromosome_arm,
      status = dominant_status
    ) %>%
    distinct() %>%
    mutate(region_type = "chromosome_arm")
}

Transform the cytoband status data

if (!(running_in_ci)) {
  final_cytoband_status_df <- combined_status_count_df %>%
    # Filter to only neutral chromosome arms and cytobands
    # that are not NA
    filter(dominant_status.arm == "neutral",
           dominant_status.cytoband != "NA") %>%
    select(
      Kids_First_Biospecimen_ID = biospecimen_id,
      region = cytoband,
      status = dominant_status.cytoband
    ) %>%
    distinct() %>%
    mutate(region_type = "cytoband")
}

Transform the gene-level status data

if (!(running_in_ci)) {
  final_gene_status_df <- combined_status_count_df %>%
    # Filter to only neutral chromosome arms and cytobands
    # that are NA or unstable (do not have a clear gain or loss call)
    filter(dominant_status.arm == "neutral",
           dominant_status.cytoband == c("NA", "unstable")) %>%
    select(
      Kids_First_Biospecimen_ID = biospecimen_id,
      region = gene_symbol,
      status = dominant_status
    ) %>%
    distinct() %>%
    mutate(region_type = "gene_symbol")
}

Combine status data for all regions

if (!(running_in_ci)) {
  # Bind the rows of each region's data.frame
  final_long_status_df <- bind_rows(final_arm_status_df,
                                    final_cytoband_status_df,
                                    final_gene_status_df) %>%
    filter(status != "uncallable")
} else {
  final_long_status_df <- final_arm_status_df %>%
    filter(status != "uncallable")
}

# Write final long status table to file
write_tsv(final_long_status_df, file.path(results_dir, "consensus_seg_most_focal_cn_status.tsv.gz"))

# Display final long status table
final_long_status_df %>%
  arrange(region_type)

Note: There are gain status calls in this final data.frame. This is not completely unexpected as there were many loss calls noticed in the data to begin with, but this may be something that is worth taking note of.

Spread status data

final_wide_status_df <- final_long_status_df %>%
  tidyr::spread(Kids_First_Biospecimen_ID, status)

# Display status data spread across samples in wide format -- each row is a
# unique region
final_wide_status_df

Session Info

sessionInfo()
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux 9 (stretch)

Matrix products: default
BLAS/LAPACK: /usr/lib/libopenblasp-r0.2.19.so

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8       
 [4] LC_COLLATE=en_US.UTF-8     LC_MONETARY=en_US.UTF-8    LC_MESSAGES=C             
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                  LC_ADDRESS=C              
[10] LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] forcats_0.4.0   stringr_1.4.0   dplyr_0.8.3     purrr_0.3.2     readr_1.3.1    
[6] tidyr_0.8.3     tibble_2.1.3    ggplot2_3.2.0   tidyverse_1.2.1

loaded via a namespace (and not attached):
 [1] Rcpp_1.0.1       cellranger_1.1.0 pillar_1.4.2     compiler_3.6.0   base64enc_0.1-3 
 [6] tools_3.6.0      digest_0.6.20    evaluate_0.14    jsonlite_1.6     lubridate_1.7.4 
[11] nlme_3.1-140     gtable_0.3.0     lattice_0.20-38  pkgconfig_2.0.2  rlang_0.4.0     
[16] cli_1.1.0        rstudioapi_0.10  yaml_2.2.0       haven_2.1.1      xfun_0.8        
[21] withr_2.1.2      xml2_1.2.0       httr_1.4.0       knitr_1.23       generics_0.0.2  
[26] hms_0.4.2        grid_3.6.0       tidyselect_0.2.5 glue_1.3.1       R6_2.4.0        
[31] readxl_1.3.1     rmarkdown_1.13   modelr_0.1.4     magrittr_1.5     htmltools_0.3.6 
[36] backports_1.1.4  scales_1.0.0     rsconnect_0.8.13 rvest_0.3.4      assertthat_0.2.1
[41] colorspace_1.4-1 labeling_0.3     stringi_1.4.3    lazyeval_0.2.2   munsell_0.5.0   
[46] broom_0.5.2      crayon_1.3.4    
LS0tCnRpdGxlOiAiRmluZCBtb3N0IGZvY2FsIHJlY3VycmVudCBjb3B5IG51bWJlciB1bml0cyIKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgogICAgdG9jOiBUUlVFCiAgICB0b2NfZmxvYXQ6IFRSVUUKYXV0aG9yOiBDaGFudGUgQmV0aGVsbCBmb3IgQUxTRiBDQ0RMCmRhdGU6IDIwMjAKcGFyYW1zOgogIGlzX2NpOiBGQUxTRQotLS0KClRoaXMgbm90ZWJvb2sgZGVmaW5lcyB0aGUgbW9zdCBmb2NhbCByZWN1cnJlbnQgY29weSBudW1iZXIgdW5pdHMgYnkgcmVtb3ZpbmcgZm9jYWwgY2hhbmdlcyB0aGF0IGFyZSB3aXRoaW4gZW50aXJlIGNocm9tb3NvbWUgYXJtIGxvc3NlcyBhbmQgZ2FpbnMuCl9Nb3N0IGZvY2FsXyBoZXJlIG1lYW5pbmc6CgotIElmIGEgY2hyb21vc29tZSBhcm0gaXMgbm90IGNsZWFybHkgZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcyAoYW5kIGlzIGNhbGxhYmxlKSB3ZSBsb29rIHRvIGRlZmluZSB0aGUgY3l0b2JhbmQgbGV2ZWwgc3RhdHVzCi0gSWYgYSBjeXRvYmFuZCBpcyBub3QgY2xlYXJseSBkZWZpbmVkIGFzIGEgZ2FpbiBvciBsb3NzIChhbmQgaXMgY2FsbGFibGUpIHdlIHRoZW4gbG9vayB0byBkZWZpbmUgdGhlIGdlbmUtbGV2ZWwgc3RhdHVzCgojIyBVc2FnZQoKVGhpcyBub3RlYm9vayBpcyBpbnRlbmRlZCB0byBiZSBydW4gZnJvbSB0aGUgY29tbWFuZCBsaW5lIHdpdGggdGhlIGZvbGxvd2luZyAoYXNzdW1lcyB5b3UgYXJlIGluIHRoZSByb290IGRpcmVjdG9yeSBvZiB0aGUgcmVwb3NpdG9yeSk6CgpgYGAKUnNjcmlwdCAtZSAicm1hcmtkb3duOjpyZW5kZXIoJ2FuYWx5c2VzL2ZvY2FsLWNuLWZpbGUtcHJlcGFyYXRpb24vMDUtZGVmaW5lLW1vc3QtZm9jYWwtY24tdW5pdHMuUm1kJywgY2xlYW4gPSBUUlVFKSIKYGBgCgojIyMgQ3V0b2ZmczogCgpgYGB7cn0KIyBUaGUgcGVyY2VudGFnZSBvZiBjYWxscyBhIHBhcnRpY3VsYXIgc3RhdHVzIG5lZWRzIHRvIGJlIAojIGFib3ZlIHRvIGJlIGNhbGxlZCB0aGUgbWFqb3JpdHkgc3RhdHVzIC0tIHRoZSBkZWNpc2lvbgojIGZvciBhIGN1dG9mZiBvZiA5MCUgaGVyZSB3YXMgbWFkZSB0byBlbnN1cmUgdGhhdCB0aGUgc3RhdHVzCiMgaXMgbm90IG9ubHkgdGhlIG1ham9yaXR5IHN0YXR1cyBidXQgaXQgaXMgYWxzbyBzaWduaWZpY2FudGx5CiMgY2FsbGVkIG1vcmUgdGhhbiB0aGUgb3RoZXIgc3RhdHVzIHZhbHVlcyBpbiB0aGUgcmVnaW9uCnBlcmNlbnRfdGhyZXNob2xkIDwtIDAuOQoKIyBUaGUgcGVyY2VudGFnZSB0aHJlc2hvbGQgZm9yIGRldGVybWluaW5nIGlmIGVub3VnaCBvZiBhIHJlZ2lvbgojIChhcm0sIGN5dG9iYW5kLCBvciBnZW5lKSBpcyBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cyAtLQojIHRoZSBkZWNpc2lvbiBmb3IgYSBjdXRvZmYgb2YgNTAlIGhlcmUgd2FzIG1hZGUgYXMgaXQgc2VlbXMgcmVhc29uYWJsZQojIHRvIGV4cGVjdCBhIHJlZ2lvbiB0byBiZSBtb3JlIHRoYW4gNTAlIGNhbGxhYmxlIGZvciBhIGRvbWluYW50IHN0YXR1cwojIGNhbGwgdG8gYmUgbWFkZQp1bmNhbGxhYmxlX3RocmVzaG9sZCA8LSAwLjUKYGBgCgojIyBTZXQgdXAKCiMjIyBMaWJyYXJpZXMgYW5kIGZ1bmN0aW9ucwoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBDdXN0b20gRnVuY3Rpb24KCmBgYHtyfQpzdGF0dXNfbWFqb3JpdHlfY2FsbGVyIDwtIGZ1bmN0aW9uKHN0YXR1c19kZiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb25fdmFyaWFibGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhdHVzX2NvbHVtbl9uYW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBlcmNlbnRfdGhyZXNob2xkX3ZhbHVlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1bmNhbGxhYmxlX3RocmVzaG9sZF92YWx1ZSA9IHVuY2FsbGFibGVfdGhyZXNob2xkKSB7CiAgIyBHaXZlbiBhIGRhdGEuZnJhbWUgd2l0aCBjeXRvYmFuZC9nZW5lLWxldmVsIGNvcHkgbnVtYmVyIHN0YXR1cyBkYXRhLAogICMgZmluZCB0aGUgZG9taW5hbnQgc3RhdHVzIG9mIHRoZSBjeXRvYmFuZC9nZW5lIHJlZ2lvbiBieSBjYWxjdWxhdGluZwogICMgdGhlIHBlcmNlbnRhZ2VzIG9mIHRoZSByZWdpb24gdGhhdCBlYWNoIGNhbGwgcmVwcmVzZW50cy4KICAjCiAgIyBBcmdzOgogICMgICBzdGF0dXNfZGY6IGRhdGEuZnJhbWUgd2l0aCBjeXRvYmFuZC9nZW5lLWxldmVsIGNvcHkgbnVtYmVyIHN0YXR1cyBkYXRhCiAgIyAgIHJlZ2lvbl92YXJpYWJsZTogc3RyaW5nIG9mIHRoZSBjb2x1bW4gbmFtZS9yZWdpb24gdG8gY2FsY3VsYXRlIGNvcHkgbnVtYmVyCiAgIyAgICAgICAgICAgICAgICAgICAgc3RhdHVzIHBlcmNlbnRhZ2VzIGZvcgogICMgICBzdGF0dXNfY29sdW1uX25hbWU6IHN0cmluZyBvZiB0aGUgY29sdW1uIG5hbWUgdGhhdCBob2xkcyB0aGUgcmVsZXZhbnQgY29weQogICMgICAgICAgICAgICAgICAgICAgICAgIG51bWJlciBzdGF0dXMgZGF0YQogICMgICBwZXJjZW50X3RocmVzaG9sZF92YWx1ZTogV2hhdCBwZXJjZW50IG9mIGNhbGxzIGEgcGFydGljdWxhciBzdGF0dXMgbmVlZHMgdG8gYmUgCiAgIyAgICAgICAgICAgICAgICAgICAgICBhYm92ZSB0byBiZSBjYWxsZWQgdGhlIG1ham9yaXR5LiAKICAjICAgdW5jYWxsYWJsZV90aHJlc2hvbGRfdmFsdWU6IGEgdGhyZXNob2xkIGZvciBkZXRlcm1pbmluZyBpZiBlbm91Z2ggb2YgYSByZWdpb24gaXMgCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICBjYWxsYWJsZSB0byBkZXRlcm1pbmUgaXRzIHN0YXR1cy4gCiAgIyAgIAogICMgUmV0dXJuOgogICMgICBzdGF0dXNfY291bnQ6IGRhdGEuZnJhbWUgd2l0aCBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCB1bmlxdWUgc3RhdHVzIGluCiAgIyAgICAgICAgICAgICAgICAgZWFjaCB1bmlxdWUgcmVnaW9uIChhcm0vY3l0b2JhbmQvZ2VuZSkgYW5kIHRoZSBkb21pbmFudAogICMgICAgICAgICAgICAgICAgIHN0YXR1cyBmb3IgdGhhdCByZWdpb24KICAKICAjIFRpZHlldmFsIGZvciB0aGVzZSBjb2x1bW5zIAogIHJlZ2lvbl9zeW0gPC0gcmxhbmc6OnN5bShyZWdpb25fdmFyaWFibGUpCiAgc3RhdHVzX3N5bSA8LSBybGFuZzo6c3ltKHN0YXR1c19jb2x1bW5fbmFtZSkKICAKICAjIEZvcm1hdCB0aGUgZGF0YSBhbmQgZ3JvdXAgaXQKICBzdGF0dXNfY291bnQgPC0gc3RhdHVzX2RmICU+JQogICAgY291bnQoISFyZWdpb25fc3ltLCAhIXN0YXR1c19zeW0pICU+JQogICAgIyBTcHJlYWQgdGhlIGRhdGEgLS0gZWFjaCByb3cgcmVwcmVzZW50cyBhIHVuaXF1ZSBjaHJvbW9zb21lIGFybQogICAgc3ByZWFkKCEhc3RhdHVzX3N5bSwgbikgJT4lCiAgICAjIFR1cm4gTkFzIGludG8gMHMKICAgIHJlcGxhY2VfbmEobGlzdCgKICAgICAgZ2FpbiA9IDAsCiAgICAgIGxvc3MgPSAwLAogICAgICBuZXV0cmFsID0gMCwKICAgICAgYW1wbGlmaWNhdGlvbiA9IDAsCiAgICAgIHVuY2FsbGFibGUgPSAwLAogICAgICB1bnN0YWJsZSA9IDAKICAgICkpICU+JQogICAgIyBHZXR0aW5nIGNvdW50cyBieSByZWdpb24gCiAgICBncm91cF9ieSghIXJlZ2lvbl9zeW0pIAogIAogICMgTGV0J3Mgc3RvcmUgdGhlIHJlZ2lvbnMgc2VwYXJhdGVseSBzbyBhcyB0byBhdm9pZCB3ZWlyZCBjb2VyY2lvbnMKICByZWdpb25fdmVjdG9yIDwtIAogICAgc3RhdHVzX2NvdW50ICU+JSAKICAgIGRwbHlyOjpwdWxsKCEhcmVnaW9uX3N5bSkKICAKICAjIENhbGN1bGF0ZSBwZXJjZW50IAogIHN0YXR1c19jb3VudCA8LSBzdGF0dXNfY291bnQgJT4lCiAgICAjIFN0b3JlIHJlZ2lvbiB2YXJpYWJsZSBjb2x1bW4gb3V0IG9mIHRoZSB3YXkgYXMgcm93bmFtZXMKICAgIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKHJlZ2lvbl92YXJpYWJsZSkgJT4lIAogICAgIyBPYnRhaW4gYSB0b3RhbCBjb3VudHMgdmFyaWFibGUgY29sdW1uCiAgICBkcGx5cjo6bXV0YXRlKHRvdGFsID0gYXBwbHkoLiwgMSwgc3VtKSkgJT4lIAogICAgIyBHZXQgdGhlIHJhdGlvIG9mIGVhY2ggc3RhdHVzIGNvdW50IHRvIHRoZSB0b3RhbAogICAgZHBseXI6Om11dGF0ZV9hdChkcGx5cjo6dmFycygtdG90YWwpLCBkcGx5cjo6ZnVucyguIC8gdG90YWwpKSAlPiUgCiAgICAjIEJyaW5nIGJhY2sgb3VyIHJlZ2lvbiB2YXJpYWJsZSBhcyBpdHMgb3duIGNvbHVtbgogICAgZHBseXI6Om11dGF0ZSghIXJlZ2lvbl92YXJpYWJsZSA6PSByZWdpb25fdmVjdG9yKQoKICAjIFRoZSBsb2dpYyBoZXJlIGlzIHRvIGRlZmluZSB0aGUgcmVnaW9uIHN0YXR1cyBiYXNlZCBvbiB0aGUgbWFqb3JpdHkgb2YgY2FsbHMKICAjIGluIHRoZSByZWdpb24gLS0gaWYgdGhlIG51bWJlciBvZiBjYWxscyBmb3IgYSBzcGVjaWZpYyBzdGF0dXMgaXMgCiAgIyByZXNwb25zaWJsZSBmb3IgbW9yZSB0aGFuIGBwZXJjZW50X3RocmVzaG9sZGAgdmFsdWUgb2YgdGhlIHRvdGFsIGNhbGxzIGluIHRoYXQKICAjIHJlZ2lvbiwgdGhlbiBpdCBpcyB1c2VkIHRvIGRlZmluZSB0aGUgcmVnaW9uJ3Mgc3RhdHVzIChleGNlcHRpb24gaXMgZm9yIHRoZSAKICAjIHVuY2FsbGFibGUgc3RhdHVzIHdoZXJlIHdlIGRlZmluZSB0aGUgcmVnaW9uIGFzIHVuY2FsbGFibGUgd2hlbiBtb3JlIHRoYW4gdGhlCiAgIyBgdW5jYWxsYWJsZV90aHJlc2hvbGRgIG9mIHRoZSBjYWxscyBpbiB0aGF0IHJlZ2lvbiBhcmUgdW5jYWxsYWJsZSkKICBpZiAoKHJlZ2lvbl92YXJpYWJsZSA9PSAiY2hyb21vc29tZV9hcm0iKSB8IChyZWdpb25fdmFyaWFibGUgPT0gImdlbmVfc3ltYm9sIikpIHsKICAgIHN0YXR1c19jb3VudCA8LSBzdGF0dXNfY291bnQgJT4lCiAgICAgIG11dGF0ZSgKICAgICAgICBkb21pbmFudF9zdGF0dXMgPSBjYXNlX3doZW4oCiAgICAgICAgICBnYWluID4gcGVyY2VudF90aHJlc2hvbGQgfiAiZ2FpbiIsCiAgICAgICAgICBsb3NzID4gcGVyY2VudF90aHJlc2hvbGQgfiAibG9zcyIsCiAgICAgICAgICBhbXBsaWZpY2F0aW9uID4gcGVyY2VudF90aHJlc2hvbGQgfiAiYW1wbGlmaWNhdGlvbiIsCiAgICAgICAgICBUUlVFIH4gIm5ldXRyYWwiCiAgICAgICAgKQogICAgICApCiAgfSBlbHNlIGlmIChyZWdpb25fdmFyaWFibGUgPT0gImN5dG9iYW5kIikgewogICAgc3RhdHVzX2NvdW50IDwtIHN0YXR1c19jb3VudCAlPiUKICAgICAgbXV0YXRlKAogICAgICAgIGRvbWluYW50X3N0YXR1cyA9IGNhc2Vfd2hlbigKICAgICAgICAgIHVuY2FsbGFibGUgPiB1bmNhbGxhYmxlX3RocmVzaG9sZCB+ICJ1bmNhbGxhYmxlIiwKICAgICAgICAgIGdhaW4gPiBwZXJjZW50X3RocmVzaG9sZH4gImdhaW4iLAogICAgICAgICAgbG9zcyA+IHBlcmNlbnRfdGhyZXNob2xkIH4gImxvc3MiLAogICAgICAgICAgbmV1dHJhbCA+IHBlcmNlbnRfdGhyZXNob2xkIH4gIm5ldXRyYWwiLAogICAgICAgICAgVFJVRSB+ICJ1bnN0YWJsZSIKICAgICAgICApCiAgICAgICkKICB9CgogIHJldHVybihzdGF0dXNfY291bnQpCn0KYGBgCgpgYGB7cn0KcGxvdF9kb21pbmFudF9zdGF0dXNfY2FsbHMgPC0gZnVuY3Rpb24oc3RhdHVzX2NvdW50X2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWdpb25fdmFyaWFibGUpIHsKICAjIEdpdmVuIGEgZGF0YS5mcmFtZSB3aXRoIHRoZSBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCByZWdpb24gYW5kIHRoZQogICMgZG9taW5hbnQgc3RhdHVzIGZvciB0aGF0IHJlZ2lvbiwgcGxvdCB0aGUgZG9taW5hbnQgc3RhdHVzIGNhbGwgb24gdGhlCiAgIyB4LWF4aXMgd2l0aCB0aGUgcGVyY2VudGFnZSB2YWx1ZXMgb24gdGhlIHktYXhpcy4KICAjCiAgIyBBcmdzOgogICMgICBzdGF0dXNfY291bnRfZGY6IGRhdGEuZnJhbWUgd2l0aCBwZXJjZW50YWdlIHZhbHVlcyBmb3IgZWFjaCB1bmlxdWUgc3RhdHVzCiAgIyAgICAgICAgICAgICAgICAgICAgaW4gZWFjaCB1bmlxdWUgcmVnaW9uIChhcm0vY3l0b2JhbmQvZ2VuZSkgYW5kIHRoZQogICMgICAgICAgICAgICAgICAgICAgIGRvbWluYW50IHN0YXR1cyBmb3IgdGhhdCByZWdpb24KICAjICAgcmVnaW9uX3ZhcmlhYmxlOiBzdHJpbmcgb2YgdGhlIHJlZ2lvbiAod2hpY2ggd2lsbCBhbHNvIGJlIGEgY29sdW1uIG5hbWUpCiAgIyAgICAgICAgICAgICAgICAgICAgdGhhdCB0aGUgZGF0YS5mcmFtZSBob2xkcyBwZXJjZW50YWdlIHZhbHVlcyBmb3IKICAjIFJldHVybjoKICAjICAgc3RhdHVzX3Bsb3Q6IHBsb3QgcmVwcmVzZW50aW5nIHRoZSBkb21pbmFudCBzdGF0dXMgY2FsbCBvbiB0aGUgeC1heGlzIGFuZAogICMgICAgICAgICAgICAgICAgdGhlIHBlcmNlbnRhZ2UgdmFsdWVzIG9uIHRoZSB5LWF4aXMKICAKICBzdGF0dXNfY291bnRfZGYgJT4lCiAgICAjIFJlbW92ZSB0aGUgdG90YWwgY29sdW1uIC0tIHdlIGRvbid0IHdhbnQgdG8gcGxvdCB0aGlzCiAgICBzZWxlY3QoLXRvdGFsKSAlPiUKICAgIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgICAjIFN0b3JlIHRoZSBub24tcGVyY2VudGFnZSB2YWx1ZSBjb2x1bW4gdmFyaWFibGUgYXMgcm93bmFtZXMKICAgIHRpYmJsZTo6Y29sdW1uX3RvX3Jvd25hbWVzKHJlZ2lvbl92YXJpYWJsZSkgJT4lCiAgICAjIEdhdGhlciB0aGUgZGF0YS5mcmFtZSB0byBoYXZlIGNvbHVtbnMgYW5kIHZhbHVlcyBpbiB0aGUgZm9ybWF0IG9mCiAgICAjIG91ciBzdGF0dXMgY2FsbCwgdGhlIHBlcmNlbnRhZ2Ugb2YgdG90YWwgY2FsbHMgdGhhdCBzdGF0dXMgY2FsbCBpcwogICAgIyByZXNwb25pc2JsZSBmb3IsIGFuZCB0aGUgZG9taW5hbnQgc3RhdHVzIGNhbGwgbWFkZSBiYXNlZCBvbiB0aGUKICAgICMgcGVyY2VudGFnZSB2YWx1ZQogICAgdGlkeXI6OmdhdGhlcihzdGF0dXMsIHBlcmNlbnQsLWRvbWluYW50X3N0YXR1cykgJT4lCiAgICAjIFBsb3Qgb3VyIGZvY2FsIHN0YXR1cyB2YWx1ZXMgb24gdGhlIHgtYXhpcyBhbmQgdGhlIHBlcmNlbnRhZ2UgdmFsdWVzCiAgICAjIG9uIHRoZSB5LWF4aXMKICAgIGdncGxvdDI6OmdncGxvdChnZ3Bsb3QyOjphZXMoeCA9IHN0YXR1cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IHBlcmNlbnQpKSArCiAgICBnZ3Bsb3QyOjpnZW9tX2ppdHRlcigpICsKICAgICMgRmFjZXQgd3JhcCBhcm91bmQgZWFjaCBkb21pbmFudCBzdGF0dXMgdmFsdWUKICAgIGdncGxvdDI6OmZhY2V0X3dyYXAoIH4gZG9taW5hbnRfc3RhdHVzKQp9CmBgYAoKCiMjIyBGaWxlcyBhbmQgZGlyZWN0b3JpZXMKCmBgYHtyfQpyZXN1bHRzX2RpciA8LSAicmVzdWx0cyIKCiMgRGVmaW5lIGEgbG9naWNhbCBvYmplY3QgZm9yIHJ1bm5pbmcgaW4gQ0kKcnVubmluZ19pbl9jaSA8LSBwYXJhbXMkaXNfY2kKYGBgCgojIyMgUmVhZCBpbiBmaWxlcwoKUmVhZCBpbiBjeXRvYmFuZCBzdGF0dXMgZmlsZSBhbmQgZm9ybWF0IGl0IGZvciB3aGF0IHdlIHdpbGwgbmVlZCBpbiB0aGlzIG5vdGVib29rLiAKCmBgYHtyfQojIFJlYWQgaW4gdGhlIGZpbGUgd2l0aCBjb25zZW5zdXMgQ04gc3RhdHVzIGRhdGEgYW5kIHRoZSBVQ1NDIGN5dG9iYW5kIGRhdGEgLS0KIyBnZW5lcmF0ZWQgaW4gYDAzLWFkZC1jeXRvYmFuZC1zdGF0dXMtY29uc2Vuc3VzLlJtZGAKY29uc2Vuc3VzX3NlZ19jeXRvYmFuZF9zdGF0dXNfZGYgPC0KICByZWFkX3RzdihmaWxlLnBhdGgoInJlc3VsdHMiLCAiY29uc2Vuc3VzX3NlZ193aXRoX3Vjc2NfY3l0b2JhbmRfc3RhdHVzLnRzdi5neiIpKSAlPiUKICAjIE5lZWQgdGhpcyB0byBub3QgaGF2ZSBgY2hyYAogIG11dGF0ZShjaHIgPSBnc3ViKCJjaHIiLCAiIiwgY2hyKSwKICAgICAgICAgY3l0b2JhbmQgPSBwYXN0ZTAoY2hyLCBjeXRvYmFuZCkpICU+JQogIHNlbGVjdCgKICAgIGNocm9tb3NvbWVfYXJtLAogICAgIyBEaXN0aW5ndWlzaCB0aGlzIGRvbWluYW50IHN0YXR1cyB0aGF0IGlzIGJhc2VkIG9uIGN5dG9iYW5kcywgZnJvbSB0aGUgc3RhdHVzIAogICAgZG9taW5hbnRfY3l0b2JhbmRfc3RhdHVzID0gZG9taW5hbnRfc3RhdHVzLAogICAgY3l0b2JhbmQsCiAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lECiAgKQpgYGAKClJlYWQgaW4gdGhlIGNocm9tb3NvbWUtbGV2ZWwgYW5kIGdlbmUtbGV2ZWwgZGF0YS4gCgpgYGB7cn0KIyBSZWFkIGluIHRoZSBhbm5vdGF0ZWQgQ04gZmlsZSAod2l0aG91dCB0aGUgVUNTQyBkYXRhKQpjb25zZW5zdXNfc2VnX2F1dG9zb21lc19kZiA8LQogIHJlYWRfdHN2KGZpbGUucGF0aChyZXN1bHRzX2RpciwgImNvbnNlbnN1c19zZWdfYW5ub3RhdGVkX2NuX2F1dG9zb21lcy50c3YuZ3oiKSkKYGBgCgpKb2luaW5nIHRoZSBnZW5lLWxldmVsLCBjeXRvYmFuZC1sZXZlbCwgYW5kIGFybS1sZXZlbCBkYXRhIGludG8gb25lIGRhdGEgZnJhbWUuCgpgYGB7cn0KY29tYmluZWRfc3RhdHVzX2RmIDwtIGNvbnNlbnN1c19zZWdfYXV0b3NvbWVzX2RmICU+JQogIGlubmVyX2pvaW4oCiAgICBjb25zZW5zdXNfc2VnX2N5dG9iYW5kX3N0YXR1c19kZiwKICAgIGJ5ID0gYygiYmlvc3BlY2ltZW5faWQiID0gIktpZHNfRmlyc3RfQmlvc3BlY2ltZW5fSUQiLCAiY3l0b2JhbmQiKQogICkKYGBgCgojIyBEZWZpbmUgbW9zdCBmb2NhbCB1bml0cwoKIyMjIERldGVybWluZSBjaHJvbW9zb21lIGFybSBzdGF0dXMKCmBgYHtyfQojIFVzZSBvdXIgY3VzdG9tIGZ1bmN0aW9uIHRvIG1ha2UgdGhlIHN0YXR1cyBjYWxscwphcm1fc3RhdHVzX2NvdW50IDwtIHN0YXR1c19tYWpvcml0eV9jYWxsZXIoCiAgY29tYmluZWRfc3RhdHVzX2RmLAogICJjaHJvbW9zb21lX2FybSIsCiAgInN0YXR1cyIsIAogIHBlcmNlbnRfdGhyZXNob2xkID0gcGVyY2VudF90aHJlc2hvbGQKKQoKIyBEaXNwbGF5IHRhYmxlCmFybV9zdGF0dXNfY291bnQgJT4lCiAgZ3JvdXBfYnkoZG9taW5hbnRfc3RhdHVzKQpgYGAKClRoZXNlIGFyZSB0aGUgY2hyb21vc29tZSBhcm1zIHRoYXQgaGF2ZSBub3QgYmVlbiBkZWZpbmVkIGFzIGdhaW4gb3IgbG9zcyAtLSB3ZSB3YW50IHRvIGRlZmluZSB0aGVpciBjeXRvYmFuZC9nZW5lLWxldmVsIHN0YXR1cwoKYGBge3J9CiMgTGV0J3MgZ2V0IGEgdmVjdG9yIG9mIHRoZSBuZXV0cmFsIGFybXMKbmV1dHJhbF9hcm1zIDwtIGFybV9zdGF0dXNfY291bnQgJT4lCiAgZmlsdGVyKGRvbWluYW50X3N0YXR1cyA9PSAibmV1dHJhbCIpICU+JSAKICBkcGx5cjo6cHVsbCgiY2hyb21vc29tZV9hcm0iKQpgYGAKCiMjIyBEZXRlcm1pbmUgY3l0b2JhbmQgc3RhdHVzCgpXZSB3YW50IHRvIGluY2x1ZGUgY3l0b2JhbmQgYW5kIGdlbmUtbGV2ZWwgY2FsbHMgZm9yIGNocm9tb3NvbWUgYXJtcyB0aGF0IGhhdmUgbm90IGJlZW4gZGVmaW5lZCBhcyBhIGdhaW4gb3IgbG9zcy4KCmBgYHtyfQojIEZpbHRlciB0aGUgYW5ub3RhdGVkIENOIGRhdGEgdG8gaW5jbHVkZSBvbmx5IG5ldXRyYWwgY2hyb21vc29tZSBhcm1zCm5ldXRyYWxfc3RhdHVzX2FybV9kZiA8LSBjb21iaW5lZF9zdGF0dXNfZGYgJT4lCiAgZmlsdGVyKGNocm9tb3NvbWVfYXJtICVpbiUgbmV1dHJhbF9hcm1zKQpgYGAKCk1ha2luZyBjeXRvYmFuZC1sZXZlbCBtYWpvcml0eSBjYWxscy4gCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKIyBOb3cgY291bnQgdGhlIGN5dG9iYW5kIGxldmVsIGNhbGxzIChmb3IgZWFjaCBzdGF0dXMgY2FsbCkgYW5kIGRlZmluZQojIHRoZSBjeXRvYmFuZCBhcyB0aGF0IHN0YXR1cyBpZiBtb3JlIHRoYW4gNTAlIG9mIHRoZSB0b3RhbCBjb3VudHMgYXJlCiMgZm9yIHRoYXQgcGFydGljdWxhciBzdGF0dXMKY3l0b2JhbmRfc3RhdHVzX2NvdW50IDwtIHN0YXR1c19tYWpvcml0eV9jYWxsZXIoCiAgbmV1dHJhbF9zdGF0dXNfYXJtX2RmLAogICJjeXRvYmFuZCIsCiAgImRvbWluYW50X2N5dG9iYW5kX3N0YXR1cyIsCiAgcGVyY2VudF90aHJlc2hvbGQgPSBwZXJjZW50X3RocmVzaG9sZAopCgojIERpc3BsYXkgdGFibGUKY3l0b2JhbmRfc3RhdHVzX2NvdW50Cn0KYGBgCgojIyMgRGV0ZXJtaW5lIGdlbmUtbGV2ZWwgc3RhdHVzCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKICAjIFRoZXNlIGFyZSB0aGUgY3l0b2JhbmRzIHRoYXQgaGF2ZSBub3QgYmVlbiBkZWZpbmVkIGFzIGdhaW4gb3IgbG9zcyAtLQogICMgd2Ugd2FudCB0byBkZWZpbmUgdGhlaXIgZ2VuZS1sZXZlbCBzdGF0dXMKICBuZXV0cmFsX2N5dG9iYW5kcyA8LSBjeXRvYmFuZF9zdGF0dXNfY291bnQgJT4lCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzICVpbiUgYygidW5zdGFibGUiLCAibmV1dHJhbCIpKSAlPiUKICAgIGRwbHlyOjpwdWxsKCJjeXRvYmFuZCIpCgogICMgRmlsdGVyIHRoZSBhbm5vdGF0ZWQgQ04gZGF0YSB0byBpbmNsdWRlIG9ubHkgdGhlc2UgY3l0b2JhbmRzCiAgbmV1dHJhbF9zdGF0dXNfY3l0b2JhbmRfZGYgPC0gY29tYmluZWRfc3RhdHVzX2RmICU+JQogICAgZmlsdGVyKGN5dG9iYW5kICVpbiUgbmV1dHJhbF9jeXRvYmFuZHMpCiAgCiAgIyBOb3cgY291bnQgdGhlIGdlbmUtbGV2ZWwgY2FsbHMgKGZvciBlYWNoIHN0YXR1cyBjYWxsKSBhbmQgZGVmaW5lCiAgIyB0aGUgZ2VuZSBhcyB0aGF0IHN0YXR1cyBpZiBtb3JlIHRoYW4gNTAlIG9mIHRoZSB0b3RhbCBjb3VudHMgYXJlCiAgIyBmb3IgdGhhdCBwYXJ0aWN1bGFyIHN0YXR1cwogIGdlbmVfc3RhdHVzX2NvdW50IDwtIHN0YXR1c19tYWpvcml0eV9jYWxsZXIobmV1dHJhbF9zdGF0dXNfY3l0b2JhbmRfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZ2VuZV9zeW1ib2wiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInN0YXR1cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVyY2VudF90aHJlc2hvbGQgPSBwZXJjZW50X3RocmVzaG9sZCkKICAKICAjIERpc3BsYXkgdGFibGUKICBnZW5lX3N0YXR1c19jb3VudAp9CmBgYAoKIyMgUGxvdCBjYWxscwoKUGxvdCB0aGUgZmluYWwgZG9taW5hbnQgc3RhdHVzIGNhbGwgb24gdGhlIHgtYXhpcyBhbmQgdGhlIHBlcmNlbnQgb2YgZWFjaCBzdGF0dXMgb24gdGhlIHktYXhpcy4KCiMjIyBQbG90IGNocm9tb3NvbWUgYXJtIHN0YXR1cyBjYWxscwoKYGBge3J9CiMgUnVuIGBwbG90X2RvbWluYW50X3N0YXR1c19jYWxsc2AgZnVuY3Rpb24gZm9yIHRoZSBjaHJvbW9zb21lIGFybSBjYWxscwpwbG90X2RvbWluYW50X3N0YXR1c19jYWxscygKICBhcm1fc3RhdHVzX2NvdW50LAogICJjaHJvbW9zb21lX2FybSIKKQpgYGAKCiMjIyBQbG90IGN5dG9iYW5kIHN0YXR1cyBjYWxscwoKYGBge3IgZmlnLndpZHRoID0gMTB9CiMgUnVuIGBwbG90X2RvbWluYW50X3N0YXR1c19jYWxsc2AgZnVuY3Rpb24gZm9yIHRoZSBjeXRvYmFuZCBjYWxscyBpZiBub3QKIyBydW5uaW5nIGluIENJCmlmICghKHJ1bm5pbmdfaW5fY2kpKSB7CiAgcGxvdF9kb21pbmFudF9zdGF0dXNfY2FsbHMoY3l0b2JhbmRfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJjeXRvYmFuZCIpCn0KYGBgCgojIyMgUGxvdCBnZW5lIHN0YXR1cyBjYWxscwoKYGBge3J9CiMgUGxvdCBpZiBub3QgcnVubmluZyBpbiBjaXJjbGVDSQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIHBsb3RfZG9taW5hbnRfc3RhdHVzX2NhbGxzKGdlbmVfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICJnZW5lX3N5bWJvbCIpCn0KYGBgCgojIyBDb21iaW5lIGFybSwgY3l0b2JhbmQsIGFuZCBnZW5lLWxldmVsIHN0YXR1cyBkYXRhCgpgYGB7cn0KIyBUaGUgbG9naWMgdmFyaWFibGUgYHJ1bm5pbmdfaW5fY2lgIGlzIG5lZWRlZCBoZXJlIGJlY2F1c2UgdGhlIENJIHRlc3RpbmcKIyBmaWxlcyBkbyBub3QgY29udGFpbiBhbnkgb2YgdGhlIGdlbmVzIGluIHRoZSBgZ2VuZV9zdGF0dXNfY291bnRgIGRhdGEuZnJhbWUKIyBnZW5lcmF0ZWQgYWJvdmUgKHdoZW4gYHJ1bm5pbmdfaW5fY2lgID09IEZBTFNFKQppZiAoIShydW5uaW5nX2luX2NpKSkgewogIGNvbWJpbmVkX3N0YXR1c19jb3VudF9kZiA8LSBjb25zZW5zdXNfc2VnX2F1dG9zb21lc19kZiAlPiUKICAgIG11dGF0ZShjaHJvbW9zb21lX2FybSA9IGdzdWIoIihwfHEpLioiLCAiXFwxIiwgY3l0b2JhbmQpKSAlPiUKICAgIGlubmVyX2pvaW4oYXJtX3N0YXR1c19jb3VudCwKICAgICAgICAgICAgICAgYnkgPSAiY2hyb21vc29tZV9hcm0iKSAlPiUKICAgIGxlZnRfam9pbihjeXRvYmFuZF9zdGF0dXNfY291bnQsCiAgICAgICAgICAgICAgYnkgPSAiY3l0b2JhbmQiLAogICAgICAgICAgICAgIHN1ZmZpeCA9IGMoIi5hcm0iLCAiLmN5dG9iYW5kIikpICU+JQogICAgbGVmdF9qb2luKGdlbmVfc3RhdHVzX2NvdW50LAogICAgICAgICAgICAgIGJ5ID0gImdlbmVfc3ltYm9sIiwKICAgICAgICAgICAgICBzdWZmaXggPSBjKCIuYXJtIiwgIi5nZW5lIikpICU+JQogICAgbXV0YXRlKAogICAgICBmb2NhbF9jYWxsID0gcGFzdGUwKAogICAgICAgIGRvbWluYW50X3N0YXR1cy5hcm0sCiAgICAgICAgIiwgIiwKICAgICAgICBkb21pbmFudF9zdGF0dXMuY3l0b2JhbmQsCiAgICAgICAgIiwgIiwKICAgICAgICBkb21pbmFudF9zdGF0dXMKICAgICAgKSwKICAgICAgIyBIZXJlIHdlIHdhbnQgdG8gZGVmaW5lIHRoZSBtb3N0IGZvY2FsIGNhbGwgYmFzZWQgb24gdGhlIGFybSwgY3l0b2JhbmQsCiAgICAgICMgYW5kIGdlbmUgc3RhdHVzIGluZm9ybWF0aW9uIC0tIGlmIGEgbG9zcy9nYWluIGlzIGRlZmluZWQgYXQgdGhlIGFybQogICAgICAjIGxldmVsIHRoZW4gdGhlIGZvY2FsIGNhbGwgd2lsbCBiZSAiYXJtX2xvc3MiIG9yICJhcm1fZ2FpbiIgcmVzcGVjdGl2ZWx5LAogICAgICAjIGFuZCBzbyBvbi4KICAgICAgZm9jYWxfY2FsbCA9IGNhc2Vfd2hlbihmb2NhbF9jYWxsID09ICJsb3NzLCBOQSwgTkEiIH4gImFybV9sb3NzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmb2NhbF9jYWxsID09ICJuZXV0cmFsLCB1bmNhbGxhYmxlLCBOQSIgfiAidW5jYWxsYWJsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9jYWxfY2FsbCA9PSAibmV1dHJhbCwgbG9zcywgTkEiIH4gImN5dG9iYW5kX2xvc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvY2FsX2NhbGwgPT0gIm5ldXRyYWwsIE5BLCBOQSIgfiAiYXJtX25ldXRyYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvY2FsX2NhbGwgPT0gIm5ldXRyYWwsIHVuc3RhYmxlLCBuZXV0cmFsIiB+ICJnZW5lX25ldXRyYWwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvY2FsX2NhbGwgPT0gIm5ldXRyYWwsIHVuc3RhYmxlLCBsb3NzIiB+ICJnZW5lX2xvc3MiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiT3RoZXIiKQogICAgKQp9IGVsc2UgewogIGNvbWJpbmVkX3N0YXR1c19jb3VudF9kZiA8LSBjb25zZW5zdXNfc2VnX2F1dG9zb21lc19kZiAlPiUKICAgIG11dGF0ZShjaHJvbW9zb21lX2FybSA9IGdzdWIoIihwfHEpLioiLCAiXFwxIiwgY3l0b2JhbmQpKSAlPiUKICAgIGlubmVyX2pvaW4oYXJtX3N0YXR1c19jb3VudCwgYnkgPSAiY2hyb21vc29tZV9hcm0iKSAlPiUKICAgIG11dGF0ZShmb2NhbF9jYWxsID0gZG9taW5hbnRfc3RhdHVzKQp9CgojIERpc3BsYXkgY29tYmluZWQgc3RhdHVzIGNvdW50cyB0YWJsZQpjb21iaW5lZF9zdGF0dXNfY291bnRfZGYgJT4lCiAgYXJyYW5nZShmb2NhbF9jYWxsKSAlPiUKICBncm91cF9ieShiaW9zcGVjaW1lbl9pZCwgZm9jYWxfY2FsbCkKYGBgCgojIyBUcmFuc2Zvcm0gZGF0YSBpbnRvIGxvbmcgZm9ybWF0CgojIyMgVHJhbmZvcm0gdGhlIGNocm9tb3NvbWUgYXJtIHN0YXR1cyBkYXRhCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKICBmaW5hbF9hcm1fc3RhdHVzX2RmIDwtIGNvbWJpbmVkX3N0YXR1c19jb3VudF9kZiAlPiUKICAgICMgRmlsdGVyIHRvIG9ubHkgbm9uLW5ldXRyYWwgY2hyb21vc29tZSBhcm1zCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzLmFybSAhPSAibmV1dHJhbCIpICU+JQogICAgc2VsZWN0KAogICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgIHJlZ2lvbiA9IGNocm9tb3NvbWVfYXJtLAogICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMuYXJtCiAgICApICU+JQogICAgZGlzdGluY3QoKSAlPiUKICAgIG11dGF0ZShyZWdpb25fdHlwZSA9ICJjaHJvbW9zb21lX2FybSIpCn0gZWxzZSB7CiAgZmluYWxfYXJtX3N0YXR1c19kZiA8LSBjb21iaW5lZF9zdGF0dXNfY291bnRfZGYgJT4lCiAgICAjIEZpbHRlciB0byBvbmx5IG5vbi1uZXV0cmFsIGNocm9tb3NvbWUgYXJtcwogICAgZmlsdGVyKGRvbWluYW50X3N0YXR1cyAhPSAibmV1dHJhbCIpICU+JQogICAgc2VsZWN0KAogICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgIHJlZ2lvbiA9IGNocm9tb3NvbWVfYXJtLAogICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMKICAgICkgJT4lCiAgICBkaXN0aW5jdCgpICU+JQogICAgbXV0YXRlKHJlZ2lvbl90eXBlID0gImNocm9tb3NvbWVfYXJtIikKfQpgYGAKCiMjIyBUcmFuc2Zvcm0gdGhlIGN5dG9iYW5kIHN0YXR1cyBkYXRhCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKICBmaW5hbF9jeXRvYmFuZF9zdGF0dXNfZGYgPC0gY29tYmluZWRfc3RhdHVzX2NvdW50X2RmICU+JQogICAgIyBGaWx0ZXIgdG8gb25seSBuZXV0cmFsIGNocm9tb3NvbWUgYXJtcyBhbmQgY3l0b2JhbmRzCiAgICAjIHRoYXQgYXJlIG5vdCBOQQogICAgZmlsdGVyKGRvbWluYW50X3N0YXR1cy5hcm0gPT0gIm5ldXRyYWwiLAogICAgICAgICAgIGRvbWluYW50X3N0YXR1cy5jeXRvYmFuZCAhPSAiTkEiKSAlPiUKICAgIHNlbGVjdCgKICAgICAgS2lkc19GaXJzdF9CaW9zcGVjaW1lbl9JRCA9IGJpb3NwZWNpbWVuX2lkLAogICAgICByZWdpb24gPSBjeXRvYmFuZCwKICAgICAgc3RhdHVzID0gZG9taW5hbnRfc3RhdHVzLmN5dG9iYW5kCiAgICApICU+JQogICAgZGlzdGluY3QoKSAlPiUKICAgIG11dGF0ZShyZWdpb25fdHlwZSA9ICJjeXRvYmFuZCIpCn0KYGBgCgojIyMgVHJhbnNmb3JtIHRoZSBnZW5lLWxldmVsIHN0YXR1cyBkYXRhCgpgYGB7cn0KaWYgKCEocnVubmluZ19pbl9jaSkpIHsKICBmaW5hbF9nZW5lX3N0YXR1c19kZiA8LSBjb21iaW5lZF9zdGF0dXNfY291bnRfZGYgJT4lCiAgICAjIEZpbHRlciB0byBvbmx5IG5ldXRyYWwgY2hyb21vc29tZSBhcm1zIGFuZCBjeXRvYmFuZHMKICAgICMgdGhhdCBhcmUgTkEgb3IgdW5zdGFibGUgKGRvIG5vdCBoYXZlIGEgY2xlYXIgZ2FpbiBvciBsb3NzIGNhbGwpCiAgICBmaWx0ZXIoZG9taW5hbnRfc3RhdHVzLmFybSA9PSAibmV1dHJhbCIsCiAgICAgICAgICAgZG9taW5hbnRfc3RhdHVzLmN5dG9iYW5kID09IGMoIk5BIiwgInVuc3RhYmxlIikpICU+JQogICAgc2VsZWN0KAogICAgICBLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lEID0gYmlvc3BlY2ltZW5faWQsCiAgICAgIHJlZ2lvbiA9IGdlbmVfc3ltYm9sLAogICAgICBzdGF0dXMgPSBkb21pbmFudF9zdGF0dXMKICAgICkgJT4lCiAgICBkaXN0aW5jdCgpICU+JQogICAgbXV0YXRlKHJlZ2lvbl90eXBlID0gImdlbmVfc3ltYm9sIikKfQpgYGAKCiMjIyBDb21iaW5lIHN0YXR1cyBkYXRhIGZvciBhbGwgcmVnaW9ucwoKYGBge3J9CmlmICghKHJ1bm5pbmdfaW5fY2kpKSB7CiAgIyBCaW5kIHRoZSByb3dzIG9mIGVhY2ggcmVnaW9uJ3MgZGF0YS5mcmFtZQogIGZpbmFsX2xvbmdfc3RhdHVzX2RmIDwtIGJpbmRfcm93cyhmaW5hbF9hcm1fc3RhdHVzX2RmLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaW5hbF9jeXRvYmFuZF9zdGF0dXNfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbmFsX2dlbmVfc3RhdHVzX2RmKSAlPiUKICAgIGZpbHRlcihzdGF0dXMgIT0gInVuY2FsbGFibGUiKQp9IGVsc2UgewogIGZpbmFsX2xvbmdfc3RhdHVzX2RmIDwtIGZpbmFsX2FybV9zdGF0dXNfZGYgJT4lCiAgICBmaWx0ZXIoc3RhdHVzICE9ICJ1bmNhbGxhYmxlIikKfQoKIyBXcml0ZSBmaW5hbCBsb25nIHN0YXR1cyB0YWJsZSB0byBmaWxlCndyaXRlX3RzdihmaW5hbF9sb25nX3N0YXR1c19kZiwgZmlsZS5wYXRoKHJlc3VsdHNfZGlyLCAiY29uc2Vuc3VzX3NlZ19tb3N0X2ZvY2FsX2NuX3N0YXR1cy50c3YuZ3oiKSkKCiMgRGlzcGxheSBmaW5hbCBsb25nIHN0YXR1cyB0YWJsZQpmaW5hbF9sb25nX3N0YXR1c19kZiAlPiUKICBhcnJhbmdlKHJlZ2lvbl90eXBlKQpgYGAKCioqTm90ZToqKiBUaGVyZSBhcmUgZ2FpbiBzdGF0dXMgY2FsbHMgaW4gdGhpcyBmaW5hbCBkYXRhLmZyYW1lLgpUaGlzIGlzIG5vdCBjb21wbGV0ZWx5IHVuZXhwZWN0ZWQgYXMgdGhlcmUgd2VyZSBtYW55IGxvc3MgY2FsbHMgbm90aWNlZCBpbiB0aGUgZGF0YSB0byBiZWdpbiB3aXRoLCBidXQgdGhpcyBtYXkgYmUgc29tZXRoaW5nIHRoYXQgaXMgd29ydGggdGFraW5nIG5vdGUgb2YuCgojIyMgU3ByZWFkIHN0YXR1cyBkYXRhCgpgYGB7cn0KZmluYWxfd2lkZV9zdGF0dXNfZGYgPC0gZmluYWxfbG9uZ19zdGF0dXNfZGYgJT4lCiAgdGlkeXI6OnNwcmVhZChLaWRzX0ZpcnN0X0Jpb3NwZWNpbWVuX0lELCBzdGF0dXMpCgojIERpc3BsYXkgc3RhdHVzIGRhdGEgc3ByZWFkIGFjcm9zcyBzYW1wbGVzIGluIHdpZGUgZm9ybWF0IC0tIGVhY2ggcm93IGlzIGEKIyB1bmlxdWUgcmVnaW9uCmZpbmFsX3dpZGVfc3RhdHVzX2RmCmBgYAoKCiMjIFNlc3Npb24gSW5mbwoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCg==